import os
import socket
import ipaddress
from concurrent.futures import ThreadPoolExecutor


def clear_screen():
    # 检查操作系统
    if os.name == "nt":  # 对于Windows
        os.system("cls")
    else:  # 对于macOS和Linux
        os.system("clear")


def get_local_ip():
    # 获取本地IP
    if os.name == "nt":
        try:
            hostname = socket.gethostname()
            local_ip = socket.gethostbyname(hostname)
            return local_ip
        except Exception as e:
            print(f"Unable to get the local IP: {e}")
            return None
    else:
        s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        try:
            # 连接到一个外部地址（不需要实际发送数据）
            s.connect(("8.8.8.8", 80))
            # 获取套接字的本地地址
            ip = s.getsockname()[0]
        finally:
            s.close()
        return ip


def scan_port(ip, port):
    # 扫描端口
    try:
        with socket.create_connection((ip, port), timeout=1):
            print(f"{ip}:{port} is open")
            return ip
    except (ConnectionRefusedError, ConnectionResetError, socket.timeout, OSError):
        return None


def scan_http(ip, port):
    # 扫描HTTP协议
    try:
        with socket.create_connection((ip, port), timeout=1) as sock:
            sock.sendall(b"GET / HTTP/1.1\r\nHost: localhost\r\n\r\n")
            response = sock.recv(1024)
            if b"HTTP" in response:
                print(f"{ip}:{port} supports HTTP protocol")
                return True
    except:
        return False


def scan_network(network_cidr, ports, scan_http_flag):
    # 扫描网络
    network = ipaddress.ip_network(network_cidr)
    open_ports = []
    print(f"Scanning {network} for ports {ports}...")
    with ThreadPoolExecutor(max_workers=100) as executor:
        futures = []
        for port in ports:
            futures.extend([executor.submit(scan_port, str(host), port) for host in network.hosts()])

        for future in futures:
            result = future.result()
            if result:
                open_ports.append(result)
                if scan_http_flag:
                    scan_http(result[0], result[1])

    return open_ports


def get_network_cidr(local_ip):
    # 整合获取网络CIDR的逻辑
    local_ip = local_ip.split(".")[:3] + ["0/24"]
    local_ip = ".".join(local_ip)
    network_cidr = input(f"请输入要扫描的网络CIDR（默认 {local_ip}）：")
    if not network_cidr:
        network_cidr = local_ip
    try:
        ipaddress.ip_network(network_cidr)  # 添加输入验证
    except ValueError:
        print("无效的网络CIDR，请重新输入。")
        return get_network_cidr(local_ip)
    return network_cidr


def get_ports():
    # 整合获取端口的逻辑
    port_input = input(f"请输入要扫描的端口（逗号分隔，默认 22）：")
    if not port_input:
        ports = [22]
    else:
        # 兼容中文逗号“，”
        port_input = port_input.replace('，', ',')
        try:
            ports = [int(p.strip()) for p in port_input.split(",")]
        except ValueError:
            print("无效的端口，请重新输入。")
            return get_ports()
    return ports


def get_scan_http_flag():
    # 整合获取HTTP扫描标志的逻辑
    scan_http_flag = input("是否要扫描HTTP协议？（y/n，默认 n）：").lower() == 'y'
    return scan_http_flag


def main():
    clear_screen()
    local_ip = get_local_ip()  # 修正变量命名错误
    print("您的本地IP是：", local_ip)
    network_cidr = get_network_cidr(local_ip)
    ports = get_ports()
    scan_http_flag = get_scan_http_flag()

    while True:
        try:
            open_ports = scan_network(network_cidr, ports, scan_http_flag)
            break
        except Exception as e:  # 改进异常处理
            clear_screen()
            print(f"错误！{e} 请重试。")
            local_ip = get_local_ip()
            print("您的本地IP是：", local_ip)
            network_cidr = get_network_cidr(local_ip)  # 简化错误处理逻辑
            ports = get_ports()
            scan_http_flag = get_scan_http_flag()

    if open_ports:
        print(f"发现以下主机的 {ports} 端口处于打开状态：")
        for ip in open_ports:
            print(ip)
    else:
        print(f"在指定网络中未发现任何主机的 {ports} 端口处于打开状态。")


if __name__ == "__main__":
    main()
